<?php
namespace GFPHP;
if (!defined('__ROOT__')) exit('Sorry,Please from entry!');

/**
 * DEBUG类
 * 存放程序运行信息
 * 方便调试项目使用
 * 创建日期 2014-08-08 15:20 PGF 一年前写的挪过来基本没动
 */
class Debug
{
    static $startTime;
    static $stopTime;
    static $msg = array();
    static $sqls = array();
    static $include = array();
    static $length;

    //-------添加程序执行信息--------
    static function add($msg, $type = 0)
    {
        if(!Config::config('debug'))
            return;
        switch ($type) {
            case 0:
                self::$msg[] = $msg;                            //把运行信息添加进去
                break;
            case '1':
                self::$include[] = $msg;                        //把包含文件添加进去
                break;
            case '2':
                self::$sqls[] = $msg;                            //把sql语句添加进去
        }
    }



    //-------获取开始微秒值-----------
    static function start()
    {
        header('X-Powered-By:GFPHP');
		if(Config::config('gzip'))
		{
			ob_start('ob_gzhandler');
        }

        //将获取的时间赋给成员属性$startTime
        self::$startTime = microtime(true);

    }

    //在脚本结束处调用获取脚本结束时间的微秒值

    static function stop()
    {
        self::$stopTime = microtime(true);   //将获取的时间赋给成员属性$stopTime
        if (Config::config('debug'))                                                        //显示DEBUG信息
            debug::message();
        if(Extension_Loaded('zlib')&&Config::config('gzip'))@Ob_End_Flush();
    }
    static function getRuntime(){
        return round((microtime(true) - self::$startTime), 4);  //计算后以4舍5入保留4位返回
    }
    //返回同一脚本中两次获取时间的差值

    static function message()
    {
        ChromePhp::GROUP('运行信息['.self::spent().']');
        foreach(self::$msg as $msg){
            ChromePhp::LOG($msg);
        }
        ChromePhp::GROUP('详情');
        ChromePhp::info('耗时: ' . self::spent() . ' sec');
        ChromePhp::info('吞吐率: ' . number_format(1/self::spent(),2) . ' rps/s');
        ChromePhp::info('内存占用: ' . round((memory_get_usage() / 1024), 4) . ' kb');
        ChromePhp::groupEnd();
        ChromePhp::groupEnd();
        ChromePhp::GROUP('包含文件['.count(self::$include).']');
        foreach(self::$include as $msg){
            ChromePhp::LOG($msg);
        }
        ChromePhp::groupEnd();
        $count = count(self::$sqls);
        if($count>0) {
            ChromePhp::GROUP('SQL语句[' . $count . ']');
            foreach (self::$sqls as $msg) {
                ChromePhp::LOG($msg);
            }
            ChromePhp::groupEnd();
        }
    }

    static function spent()
    {

        return round((self::$stopTime - self::$startTime), 4);  //计算后以4舍5入保留4位返回

    }
}
class ChromePhp
{
    /**
     * @var string
     */
    const VERSION = '4.1.0';
    /**
     * @var string
     */
    const HEADER_NAME = 'X-ChromeLogger-Data';
    /**
     * @var string
     */
    const BACKTRACE_LEVEL = 'backtrace_level';
    /**
     * @var string
     */
    const LOG = 'log';
    /**
     * @var string
     */
    const WARN = 'warn';
    /**
     * @var string
     */
    const ERROR = 'error';
    /**
     * @var string
     */
    const GROUP = 'group';
    /**
     * @var string
     */
    const INFO = 'info';
    /**
     * @var string
     */
    const GROUP_END = 'groupEnd';
    /**
     * @var string
     */
    const GROUP_COLLAPSED = 'groupCollapsed';
    /**
     * @var string
     */
    const TABLE = 'table';
    /**
     * @var string
     */
    protected $_php_version;
    /**
     * @var int
     */
    protected $_timestamp;
    /**
     * @var array
     */
    protected $_json = array(
        'version' => self::VERSION,
        'columns' => array('log', 'backtrace', 'type'),
        'rows' => array()
    );
    /**
     * @var array
     */
    protected $_backtraces = array();
    /**
     * @var bool
     */
    protected $_error_triggered = false;
    /**
     * @var array
     */
    protected $_settings = array(
        self::BACKTRACE_LEVEL => 1
    );
    /**
     * @var ChromePhp
     */
    protected static $_instance;
    /**
     * Prevent recursion when working with objects referring to each other
     *
     * @var array
     */
    protected $_processed = array();
    /**
     * constructor
     */
    private function __construct()
    {
        $this->_php_version = phpversion();
        $this->_timestamp = $this->_php_version >= 5.1 ? $_SERVER['REQUEST_TIME'] : time();
        $this->_json['request_uri'] = $_SERVER['REQUEST_URI'];
    }
    /**
     * gets instance of this class
     *
     * @return ChromePhp
     */
    public static function getInstance()
    {
        if (self::$_instance === null) {
            self::$_instance = new self();
        }
        return self::$_instance;
    }

    public static function log()
    {
        $args = func_get_args();
        return self::_log('', $args);
    }

    public static function warn()
    {
        $args = func_get_args();
        return self::_log(self::WARN, $args);
    }
    /**
     * logs an error to the console
     *
     * @param mixed $data,... unlimited OPTIONAL number of additional logs [...]
     * @return void
     */
    public static function error()
    {
        $args = func_get_args();
        return self::_log(self::ERROR, $args);
    }
    /**
     * sends a group log
     *
     * @param string value
     */
    public static function group()
    {
        $args = func_get_args();
        return self::_log(self::GROUP, $args);
    }
    /**
     * sends an info log
     *
     * @param mixed $data,... unlimited OPTIONAL number of additional logs [...]
     * @return void
     */
    public static function info()
    {
        $args = func_get_args();
        return self::_log(self::INFO, $args);
    }
    /**
     * sends a collapsed group log
     *
     * @param string value
     */
    public static function groupCollapsed()
    {
        $args = func_get_args();
        return self::_log(self::GROUP_COLLAPSED, $args);
    }
    /**
     * ends a group log
     *
     * @param string value
     */
    public static function groupEnd()
    {
        $args = func_get_args();
        return self::_log(self::GROUP_END, $args);
    }
    /**
     * sends a table log
     *
     * @param string value
     */
    public static function table()
    {
        $args = func_get_args();
        return self::_log(self::TABLE, $args);
    }

    /**
     * internal logging call
     *
     * @param string $type
     * @param array $args
     */
    protected static function _log($type, array $args)
    {
        // nothing passed in, don't do anything
        if (count($args) == 0 && $type != self::GROUP_END) {
            return;
        }
        $logger = self::getInstance();
        $logger->_processed = array();
        $logs = array();
        foreach ($args as $arg) {
            $logs[] = $logger->_convert($arg);
        }
        $backtrace = debug_backtrace(false);
        $level = $logger->getSetting(self::BACKTRACE_LEVEL);
        $backtrace_message = 'unknown';
        if (isset($backtrace[$level]['file']) && isset($backtrace[$level]['line'])) {
            $backtrace_message = $backtrace[$level]['file'] . ' : ' . $backtrace[$level]['line'];
        }
        $logger->_addRow($logs, $backtrace_message, $type);
    }
    /**
     * converts an object to a better format for logging
     *
     * @param Object
     * @return array
     */
    protected function _convert($object)
    {
        // if this isn't an object then just return it
        if (!is_object($object)) {
            return $object;
        }
        //Mark this object as processed so we don't convert it twice and it
        //Also avoid recursion when objects refer to each other
        $this->_processed[] = $object;
        $object_as_array = array();
        // first add the class name
        $object_as_array['___class_name'] = get_class($object);
        // loop through object vars
        $object_vars = get_object_vars($object);
        foreach ($object_vars as $key => $value) {
            // same instance as parent object
            if ($value === $object || in_array($value, $this->_processed, true)) {
                $value = 'recursion - parent object [' . get_class($value) . ']';
            }
            $object_as_array[$key] = $this->_convert($value);
        }
        $reflection = new ReflectionClass($object);
        // loop through the properties and add those
        foreach ($reflection->getProperties() as $property) {
            // if one of these properties was already added above then ignore it
            if (array_key_exists($property->getName(), $object_vars)) {
                continue;
            }
            $type = $this->_getPropertyKey($property);
            if ($this->_php_version >= 5.3) {
                $property->setAccessible(true);
            }
            try {
                $value = $property->getValue($object);
            } catch (ReflectionException $e) {
                $value = 'only PHP 5.3 can access private/protected properties';
            }
            // same instance as parent object
            if ($value === $object || in_array($value, $this->_processed, true)) {
                $value = 'recursion - parent object [' . get_class($value) . ']';
            }
            $object_as_array[$type] = $this->_convert($value);
        }
        return $object_as_array;
    }
    /**
     * takes a reflection property and returns a nicely formatted key of the property name
     *
     * @param ReflectionProperty
     * @return string
     */
    protected function _getPropertyKey(ReflectionProperty $property)
    {
        $static = $property->isStatic() ? ' static' : '';
        if ($property->isPublic()) {
            return 'public' . $static . ' ' . $property->getName();
        }
        if ($property->isProtected()) {
            return 'protected' . $static . ' ' . $property->getName();
        }
        if ($property->isPrivate()) {
            return 'private' . $static . ' ' . $property->getName();
        }
    }
    /**
     * adds a value to the data array
     *
     * @var mixed
     * @return void
     */
    protected function _addRow(array $logs, $backtrace, $type)
    {
        // if this is logged on the same line for example in a loop, set it to null to save space
        if (in_array($backtrace, $this->_backtraces)) {
            $backtrace = null;
        }
        // for group, groupEnd, and groupCollapsed
        // take out the backtrace since it is not useful
        if ($type == self::GROUP || $type == self::GROUP_END || $type == self::GROUP_COLLAPSED) {
            $backtrace = null;
        }
        if ($backtrace !== null) {
            $this->_backtraces[] = $backtrace;
        }
        $row = array($logs, $backtrace, $type);
        $this->_json['rows'][] = $row;
        $this->_writeHeader($this->_json);
    }
    protected function _writeHeader($data)
    {
        header(self::HEADER_NAME . ': ' . $this->_encode($data));
    }
    /**
     * encodes the data to be sent along with the request
     *
     * @param array $data
     * @return string
     */
    protected function _encode($data)
    {
        return base64_encode(utf8_encode(json_encode($data)));
    }
    /**
     * adds a setting
     *
     * @param string key
     * @param mixed value
     * @return void
     */
    public function addSetting($key, $value)
    {
        $this->_settings[$key] = $value;
    }
    /**
     * add ability to set multiple settings in one call
     *
     * @param array $settings
     * @return void
     */
    public function addSettings(array $settings)
    {
        foreach ($settings as $key => $value) {
            $this->addSetting($key, $value);
        }
    }
    /**
     * gets a setting
     *
     * @param string key
     * @return mixed
     */
    public function getSetting($key)
    {
        if (!isset($this->_settings[$key])) {
            return null;
        }
        return $this->_settings[$key];
    }
}
//====================    END Debug.class.php      ========================//